option autorun on
' night_light.bas
' July 7, 2017
' MMBasic version 5.04.05
' automatic, time adjusting night light using the
' Explore-28, the Evil Mad Scientist simple relay
 shield, the Voltaic Systems high intensity USB
 Flashlight, Flexlight or Touchlight, and any
Micromite-compatible RTC

 www.shopevilmafscientist.com
 www.voltaicsystems.com
 williams.best.vwh.net

 RTC pin-to-CPU pin wiring
 -------------------------
 SCL ==> 17, SDA ==> 18 (28 pin)
 SCL ==> 44, SDA ==> 1 (44 pin)

 relay shield ==> pin 15

 status LED ==> pin 14

''''''''''''''''''''''

 IMPORTANT! set the RTC to local "standard" civil time
          do not include DST when setting the RTC

 =======================
 Cape Canaveral, Florida
 =======================
 time zone
tzone = -5

 geographic latitude (degrees)
obslat = 28.0 + 23.0 / 60.0 + 46.0 / 3600.0

 geographic longitude (degrees; positive east, negative west)
obslong = -(80.0 + 35.0 / 60.0 + 54.0 / 3600.0)

 Sun's zenith for sunrise/sunset
 -------------------------------
 official     = 90 degrees 50'
 civil        = 96 degrees
 nautical     = 102 degrees
 astronomical = 108 degrees
 useoffical zenith (degrees)

zenith = 90.0 + 50.0 / 60.0

 initialize relay "coil energized" pin

setpin 15, dout

 initialize status LED
setpin 14, dout

do

 turn on status LED
pin(14) = 1

 extract current epoch from RTC
rtcgettime

hours = val(left$(time$, 2))
minutes = val(mid$(time$, 4, 5))
month = val(mid$(date$, 4, 5))
day = val(left$(date$, 2))
year = val(mid$(date$, 7, 10))

 current local civil time (hours)

lct_hours = hours + minutes / 60.0

 compute local civil time of sunrise (hours)

rsflag% = 1
riseset(month, day, year, lct_rise)

 compute local civil time of sunset (hours)

rsflag% = 0
riseset(month, day, year, lct_set)

 determine state of night light

if (lct_hours>= lct_rise - 0.25 and lct_hours<= lct_set + 0.25) then

pin(15) = 0

else

pin(15) = 1

endif

 turn off status LED

pin(14) = 0

 pause 10 seconds between calculations

pause 10000

loop

end

'''''''''''''''''''''''''''''''

sub riseset(month, day, year, lct)

 compute rise and set times of the sun

 Almanac for Computers, 1990
 published by Nautical Almanac Office
 United States Naval Observatory
 Washington, DC 20392

 input

  month = calendar month
  day   = calendar day
  year  = calendar year

 output

lct = local civil time of sun rise or set

'''''''''''''''''''''''''''''''''''''''''''

 calculate the day of the year

day_of_year(month, day, year, n)

 convert observer longitude to hour value

longHour = obslong / 15.0

 calculate an approximate event time

  if (rsflag% = 1) then

 rising time

    t = n + ((6.0 - longHour) / 24.0)

  else

 setting time

    t = n + ((18.0 - longHour) / 24.0)

  endif

 Sun's mean anomaly (degrees)

manom = 0.9856 * t - 3.289

 Sun's true longitude (degrees)

  L = manom + (1.916 * sin(rad(manom))) + (0.020 * sin(rad(2.0 * manom))) + 282.634

  if (L < 0.0) then L = 360.0 + L

  if (L > 360.0) then L = L - 360.0

 right ascension of the sun (degrees)

rasc = deg(atn(0.91764 * tan(rad(L))))

  if (rasc< 0.0) then rasc = 360.0 + rasc

  if (rasc> 360.0) then rasc = rasc - 360.0

 right ascension value needs to be in the same quadrant as L

Lquadrant  = (int(L / 90.0)) * 90.0

RAquadrant = (int(rasc / 90.0)) * 90.0

rasc = rasc + (Lquadrant - RAquadrant)

  if (rasc< 0.0) then rasc = 360.0 + rasc

  if (rasc> 360.0) then rasc = rasc - 360.0

 convert right ascension to hours

rasc = rasc / 15.0

 sine and cosine of the declination of the sun

sinDec = 0.39782 * sin(rad(L))

cosDec = cos(asin(sinDec))

 cosine of the local hour angle of the sun

cosH = (cos(rad(zenith)) - (sinDec * sin(rad(obslat)))) / (cosDec * cos(rad(obslat)))

  if (cosH> 1.0) then

    print "the sun never rises on this location on this date"

    return

  endif

  if (cosH< -1.0) then

    print "the sun never sets on this location on this date"

    return

  endif

 finish calculating local hour angle of the sun

  if (rsflag% = 1) then

 rising time

    H = 360.0 - deg(acos(cosH))

  else

 setting time

    H = deg(acos(cosH))

  endif

 convert local hour angle into hours

  H = H / 15.0

 calculate local mean time

lmt = H + rasc - (0.06571 * t) - 6.622

 calculate UTC

utc = lmt - longHour

  if (utc< 0.0) then utc = 24.0 + utc

  if (utc> 24.0) then utc = utc - 24.0

 determine local civil time of rise or set (hours)

lct = utc + tzone

  if (lct< 0.0) then lct = 24.0 + lct

  if (lct> 24.0) then lct = lct - 24.0

end sub

'''''''''''''''''''''''''''''''''''

Sub day_of_week(month, day, year, dow)

 calculate day of the week using
TZAdvantage's (modified) algorithm

 input

  month = calendar month
  day   = calendar day
  year  = calendar year

 output

dow = day of the week

'''''''''''''''''''''''

  a = Int((14-month) / 12)

  m = month + 12 * a - 2

  y = year - a

  d = day

dow = (d + y + Int(y / 4) - Int(y / 100) + Int(y / 400) + Int(31 * m / 12)) Mod 7

End Sub

'''''''''''''''''''''''''''''''''''

sub day_of_year(month, day, year, doy)

 calculate day of the year

 input

  month = calendar month
  day   = calendar day
  year  = calendar year

 output

doy = day of the year

'''''''''''''''''''''''

  n1 = fix(275.0 * month / 9.0)

  n2 = fix((month + 9.0) / 12.0)

  n3 = (1.0 + fix((year - 4.0 * fix(year / 4.0) + 2.0) / 3.0))

doy = n1 - (n2 * n3) + day - 30.0

end sub

'''''''''''''''

FUNCTION mod360(x)

 modulo 360 degrees function

''''''''''''''''''''''''''''

  a = x - 360.0 * FIX(x / 360.0)

  IF (a < 0.0) THEN a = a + 360.0

  mod360 = a

END FUNCTION

''''''''''''''

FUNCTION mod24(x)

 modulo 24 hours function

'''''''''''''''''''''''''

  a = x - 24.0 * FIX(x / 24.0)

  IF (a < 0.0) THEN a = a + 24.0

  mod24 = a

END FUNCTION
